#pragma once
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <functional>

#include "Log.hpp"
#include "InetAddr.hpp"
#include "threadpool.hpp"

const int defdultport = 8888;

using task_t = std::function<void()>;

class Server
{
public:
    Server(const int& port = defdultport):_port(port)
    {}
    ~Server()
    {}

    // class ThreadData
    // {
    // public:
    //     ThreadData(int Td_sockfd,const InetAddr Td_addr, Server* Td_self)
    //         :_Td_sockfd(Td_sockfd), _Td_addr(Td_addr), _Td_self(Td_self)
    //     {}

    // public:
    //     int _Td_sockfd;
    //     InetAddr _Td_addr;
    //     Server* _Td_self;
    // };

    void Init()
    {
        // 创建套接字
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_listen_sockfd < 0)
        {
            std::cerr << "socket failed" << std::endl;
            exit(1);
        }
        std::cout << "Socket Success" << std::endl;

        // bind与端口建立连接
        struct sockaddr_in addr;
        memset(&addr, 0 ,sizeof(addr));
        addr.sin_port = htons(_port);
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = INADDR_ANY;

        if(bind(_listen_sockfd, (sockaddr*)&addr, sizeof(addr)) < 0)
        {
            std::cerr << "bind failed" << std::endl;
            close(_listen_sockfd);
            exit(2);
        }
        std::cout << "bind Success" << std::endl;

        // 设置监听
        if(listen(_listen_sockfd, 10) < 0)
        {
            std::cerr << "listen failed" << std::endl;
            close(_listen_sockfd);
            exit(3);
        }
        std::cout << "Listen Success" << std::endl;
    }

    void Close()
    {
        if(_listen_sockfd > 0)
        {
            close(_listen_sockfd);
        }
    }

    // 因为是面向连接的，所以要想办法处理客户端sockfd
    void Start()
    {
        while (true)
        {
            // 接收从客户端发来的连接，得到用于通信的fd
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int client_sockfd = accept(_listen_sockfd, (sockaddr*)&client, &len);
            if(client_sockfd < 0)
            {
                std::cerr << "accept failed" << std::endl;
                continue;
            }
            std::cout << "Get a connect" << std::endl;

            // 然后就是client_sockfd与客户端进行通信
            // >需要注意的是，因为提供给客户端的是长服务，所以如果只是单线程的话，那就无法进入下一次循环
            // 就一直停留在服务里了，这样导致的结果就是最终只能有一个客户端与服务端通信，这是绝对不允许的。
            // 因此就有以下的解决方法


            // version 1： 多进程方法,需要对文件描述符做额外操作
            pid_t pid = fork();
            if(pid == 0)
            {
                // 子进程
                // 因为这里是子进程，会继承父进程的文件描述符表。同时又因为子进程以后不再需要使用_listen_sockfd，所以
                // 这里建议直接关闭，避免误操作
                ::close(_listen_sockfd);

                // 还需要注意的是，对于子进程来说，父进程是需要主动wait来回收子进程的资源的，但是wait也是阻塞等待，会阻止进入下一个循环
                // 所以需要对子进程资源回收做处理，这里有很多方法，比如说信号之类的，但是有一个方法比较好，就是将子进程变成孤儿进程
                // 交给OS进行管理和回收
                if(fork() > 0) exit(0);     //父进程退出，创建新的没有父进程的子进程
                Service(client_sockfd, client); // 新的子进程，然后变成孤儿进程。
                close(client_sockfd);

                // 服务结束，子进程必须要退出，不然子进程就还没结束，会进入循环导致错误。
                exit(0);
            }
            // 因为父进程会不断开辟新的子进程，同时父进程已经把用于通信的client_sockfd交给了子进程，不再需要用到它了。
            // 并且以后创建的子进程还会继承它的文件描述符表，所以在这里直接close(client_sockfd),
            // 避免文件描述符溢出，同时让新开辟出来的文件描述符都是同一个数。
            ::close(client_sockfd);
            client_sockfd = 0;

            // version 2: 多线程方法，
            // 需要注意的是，对于线程，同进程一样，当线程结束时也需要主线程进行pthread_join()来回收线程资源
            // 不过对于多线程，是不需要主线程关闭文件描述符的，也不能关闭，因为线程之间的数据是共享的，如果关闭了
            // 那么子线程的client_sockfd也就关闭了。

            // pthread_t tid;
            // Server::ThreadData* td = new Server::ThreadData(client_sockfd, client, this);
            // pthread_create(&tid, nullptr, Execute, td);
            

            // version 3: 线程池方法
            // 既然多线程都有了，那么线程池当然也可以
            // task_t task = std::bind(&Server::Service, this, client_sockfd, client);
            // ThreadPool<task_t>::GetInstance()->Equeue(task);
        }
    }

    // 命名空间::函数名()`
    // static void* Execute(void* arg)
    // {
    //     pthread_detach(pthread_self());
    //     ThreadData* td = static_cast<ThreadData*>(arg);
    //     td->_Td_self->Service(td->_Td_sockfd, td->_Td_addr);
    //     delete td;

    //     return nullptr;
    // }

    void Service(int sockfd, InetAddr addr)
    {
        // 长服务
        while(true)
        {
            // 先接收消息
            char buffer[1024];
            ssize_t n = recv(sockfd, buffer, sizeof(buffer), 0);
            std::cout << "Get message from " << addr.AddrStr() << ": " << buffer << std::endl; 
            if(n > 0)
            {
                // 接收成功后在再发送消息
                std::string message = "Echo#";
                buffer[n] = 0;
                message += buffer;
                ssize_t m = send(sockfd, message.c_str(), message.size(), 0);
                if(m < 0 || m == 0)
                {
                    std::cerr << "send faild" << std::endl;
                    break;
                }
            }
            else if(n == 0)
            {
                std::cerr << "client quit" << std::endl;
                break;
            }
            else
            {
                std::cerr << "recv failed" << std::endl;
                break;
            }
        }
    }



private:
    int _listen_sockfd;
    uint16_t _port;
};
